
#include <stdio.h>

#include <BRange>
#include <BStyle>
#include <BFont>
#include <BEvent>
#include <BPainter>
#include <BKeyEvent>
#include <BCharEvent>
#include <BMouseEvent>
#include <BSystem>

#include <BDateEdit>
#include "member_BDateEdit.h"

using namespace BWE;

#define member					(*(member_BDateEdit*)_ptr)
#define member_allocate()		_ptr = new member_BDateEdit(this)
#define member_release()		delete (member_BDateEdit*)_ptr

BDateEdit::BDateEdit(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BDateEdit::BDateEdit(const BDate& date, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setDate(date);
}
BDateEdit::~BDateEdit()
{
	member_release();
}

void BDateEdit::setDate(const BDate& date)
{
	if (member.date != date)
	{
		member.date = date;
		member.dirtyText = true;
		emit(Signal_DateChanged, member.date);
		this->fresh();
	}
}
const BDate& BDateEdit::date() const
{
	return member.date;
}

void BDateEdit::setDisplayFormat(const BString& displayFormat)
{
	if (member.format != displayFormat)
	{
		member.format = displayFormat;
		member.format.lower();
		member.dirtyText = true;
		this->fresh();
	}
}
const BString& BDateEdit::displayFormat() const
{
	return member.format;
}

void BDateEdit::setAlign(Align align)
{
	if (member.align != align)
	{
		member.align = align;
		member.dirty = true;
		this->fresh();
	}
}
Align BDateEdit::align() const
{
	return member.align;
}

void BDateEdit::setReadOnly(bool readOnly)
{
	if (member.readOnly != readOnly)
	{
		member.readOnly = readOnly;
		this->fresh();
	}
}
bool BDateEdit::readOnly() const
{
	return member.readOnly;
}

void BDateEdit::setSynchro(bool synchro)
{
	if (member.synchro != synchro)
	{
		member.synchro = synchro;
		this->fresh();
	}
}
bool BDateEdit::synchro() const
{
	return member.synchro;
}

BSize BDateEdit::sizeHint() const
{
	BSize hsize = BWidget::sizeHint();
	const BFont* font = this->font();
	if (font)
	{
		int cw = font->codeWidth('0');
		hsize.width() += cw * 10;
		hsize.height() += font->size() + 3;
	}
	return hsize;
}

void BDateEdit::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	this->fresh();
}
void BDateEdit::updateEvent(const BEvent& event)
{
	if (member.synchro)
	{
		BDate date = BSystem::LocalDate();
		this->setDate(date);
	}
	if (this->focused() && member.inputCount == 0)
	{
		bool blinkShown = true;
		if (member.blinkTime > 0)
		{
			BReal now = event.time();
			BReal time = now - member.blinkTick;
			blinkShown = time <= member.blinkTime / 2;
			if (time > member.blinkTime)
				member.blinkTick = now;
		}
		if (member.blinkShown != blinkShown)
		{
			member.blinkShown = blinkShown;
			this->fresh();
		}
	}
}

void BDateEdit::styleEvent(const BEvent& event)
{
	if (const BStyle* style = this->realStyle())
	{
		const BFont* font = style->font();
		if (font)
			member.textHeight = font->size();
	}
	BWidget::styleEvent(event);
}
void BDateEdit::freshEvent(const BEvent& event)
{
	BWidget::freshEvent(event);
	if (member.dirtyText && !member.edited)
	{
		member.text = member.date.toString(member.format);
		member.dirtyText = false;
		member.dirty = true;
	}
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
}
void BDateEdit::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	//painter.drawContent();

	if (this->focused() && member.inputCount > 0)
	{
		int left = member.rects[member.inputIndex].x();
		int right = member.rects[member.inputIndex + member.inputCount].x();
		painter.setColor(Color_Background_Checked);
		painter.fillRect(left, member.textRect.top(), right - left, member.textRect.height());
	}

	if (painter.setColor(Color_Text))
	{
		painter.drawText(member.textRect, member.text);
	}

	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();

	if (this->focused() && member.blinkShown && member.inputCount == 0)
	{
		const BRect& rect = member.rects[member.inputIndex];
		int x = rect.left();
		int y = rect.center().y();
		int hh = member.textHeight / 2;
		if (painter.setColor(Color_Indicator))
		{
			painter.drawLine(x, y - hh, x, y + hh);
			painter.drawLine(x - 1, y - hh, x + 2, y - hh);
			painter.drawLine(x - 1, y + hh, x + 2, y + hh);
		}
	}
}

void BDateEdit::focusEvent(const BEvent& event)
{
	if (!member.readOnly && this->focused())
	{
		member.focusedTime = event.time();
		member.inputIndex = 0;
		member.inputCount = member.text.count();
		this->fresh();
	}
	if (!member.readOnly && !this->focused())
	{
		if (member.edited)
		{
			emit(Signal_EditFinished, member.date);
			member.edited = false;
		}
		this->fresh();
	}
	BWidget::focusEvent(event);
}
void BDateEdit::keyDownEvent(const BKeyEvent& keyEvent)
{
	Key key = keyEvent.key();
	switch (key)
	{
	case Key_Left:
	{
		if (member.inputCount > 0)
		{
			member.inputCount = 0;
			this->fresh();
		}
		else if (member.inputIndex > 0)
		{
			member.inputIndex--;
			this->fresh();
		}
		break;
	}
	case Key_Right:
	{
		if (member.inputCount > 0)
		{
			member.inputIndex += member.inputCount;
			member.inputCount = 0;
			this->fresh();
		}
		else if (member.inputIndex < member.text.size())
		{
			member.inputIndex++;
			this->fresh();
		}
		break;
	}
	case Key_Back:
	{
		if (member.inputCount > 0)
		{
			if (member.text.remove(member.inputIndex, member.inputCount))
			{
				member.inputCount = 0;
				member.edited = true;
				BDate date(member.text);
				this->setDate(date);
				emit(Signal_DateEdited, member.date);
				this->fresh();
			}
		}
		else
		{
			member.inputIndex--;
			if (member.text.remove(member.inputIndex))
			{
				member.edited = true;
				BDate date(member.text);
				this->setDate(date);
				emit(Signal_DateEdited, member.date);
				this->fresh();
			}
		}
		break;
	}
	case Key_Delete:
	{
		if (member.inputCount > 0)
		{
			if (member.text.remove(member.inputIndex, member.inputCount))
			{
				member.inputCount = 0;
				member.edited = true;
				BDate date(member.text);
				this->setDate(date);
				emit(Signal_DateEdited, member.date);
				this->fresh();
			}
		}
		else
		{
			if (member.text.remove(member.inputIndex))
			{
				member.edited = true;
				BDate date(member.text);
				this->setDate(date);
				emit(Signal_DateEdited, member.date);
				this->fresh();
			}
		}
		break;
	}
	case Key_Enter:
	{
		this->setFocused(false);
		break;
	}
	}
	BWidget::keyDownEvent(keyEvent);
}
void BDateEdit::keyUpEvent(const BKeyEvent& keyEvent)
{
	this->fresh();
	BWidget::keyUpEvent(keyEvent);
}
void BDateEdit::charEvent(const BCharEvent& charEvent)
{
	BWidget::charEvent(charEvent);
	BCode code = charEvent.code();
	if (code == ':' || (code >= '0' && code <= '9'))
	{
		if (member.inputCount > 0)
		{
			member.text.remove(member.inputIndex, member.inputCount);
			member.inputCount = 0;
		}
		if (member.text.insert(member.inputIndex, code))
		{
			member.inputIndex++;
			member.edited = true;
			BDate date(member.text);
			this->setDate(date);
			emit(Signal_DateEdited, member.date);
			this->fresh();
		}
	}
}

void BDateEdit::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex != -1)
	{
		member.hoverIndex = -1;
		this->fresh();
	}
	BWidget::mouseEnterEvent(mouseEvent);
}
void BDateEdit::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex != -1)
	{
		member.hoverIndex = -1;
		this->fresh();
	}
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BDateEdit::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.time() - member.focusedTime < (BReal)0.3)
		return;
	if (mouseEvent.button() == Button_Left)
	{
		const BPoint& pos = mouseEvent.pos();
		int hoverIndex = member.hoverIndex;
		if (member.inputCount > 0 || member.inputIndex != member.hoverIndex)
		{
			member.inputIndex = member.hoverIndex;
			member.inputCount = 0;
			this->fresh();
		}
		member.pressed = true;
		member.pressedIndex = member.hoverIndex;
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BDateEdit::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	member.pressed = false;
	this->fresh();
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BDateEdit::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	int index = 0;
	while (index < member.rects.size())
	{
		const BRect& rect = member.rects[index];
		if (rect.center().x() > pos.x())
			break;
		index++;
	}
	if (member.hoverIndex != index)
	{
		member.hoverIndex = index;
		this->fresh();
	}
	if (member.pressed)
	{
		member.inputIndex = bMin(member.pressedIndex, member.hoverIndex);
		member.inputCount = abs(member.pressedIndex - member.hoverIndex);
		this->fresh();
	}
	BWidget::mouseMoveEvent(mouseEvent);
}
bool BDateEdit::mouseWheelEvent(const BMouseEvent& mouseEvent)
{
	if (member.readOnly)
		return BWidget::mouseWheelEvent(mouseEvent);
	int whell = mouseEvent.wheel();
	BDate date = member.date + whell;
	this->setDate(date);
	emit(Signal_DateEdited, member.date);
	member.edited = false;
	this->fresh();
	return true;
}
void BDateEdit::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex >= 0)
	{
		if (member.text[member.hoverIndex] == ':')
		{
			member.inputIndex = member.hoverIndex;
			member.inputCount = 1;
			this->fresh();
		}
		else
		{
			int begin = member.text.rfind('-', member.hoverIndex) + 1;
			int end = member.text.find('-', member.hoverIndex);
			member.inputIndex = begin;
			member.inputCount = end - begin;
			this->fresh();
		}
	}
	BWidget::mouseDoubleClickEvent(mouseEvent);
}

